#include "video-graphics.h"
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include "sprite.h"
#include "pixmap.h"
#include "kbc.h"
#include "timer.h"
#include "music.h"
#include "ints.h"
#include "queue.h"
#include "rtc.h"
#include "GQueue.h"
#include <time.h>
#include <stdlib.h>
#include <string.h>

typedef struct{

	char* nome;
	int pontuacao;
	int vidas;
	Sprite * vimg[3];

}Jogador;

typedef struct{ 
	int  number;
	char name[15];
}Score;

#define ESC 0x01



Queue keys_queue;
void kbd_isr(void);
extern Bool kika;

void asm_wait(void);

int  compare(Score *, Score *);

void printscore(Score sc);

typedef int (*compfn)(const void*, const void*);

_go32_dpmi_seginfo old_rtc_irq;

GQueue* music_queue;

typedef enum {INACTIVE, PLAY, PAUSE, STOPPED} MUSIC_STATE;
volatile MUSIC_STATE state = INACTIVE;
volatile int ms_count = 0;

Song* bgm;
int song_note_ind = 0;

void rtc_isr(void)
{
	Byte cause = read_rtc(RTC_STAT_C);
	
	if(cause & RTC_PF) // periodic interrupt
	{
		switch(state)
		{
			case INACTIVE:
				break;

			case STOPPED:
				if(!isEmptyGQueue(music_queue))
				{
					Note* nt = (Note*)getGQueue(music_queue);
					if(nt->freq != -1)
					{
						timer_load(TIMER_2, get_divider(notes_frequency[nt->freq]));
						speaker_on();
					}
					ms_count = nt->dur;
					state = PLAY;
				}
				break;
				
			case PLAY:
				if(ms_count-- == 0)
				{
					speaker_off();
					//ms_count = 20;
					ms_count = bgm->pause;
					state = PAUSE;
					if(song_note_ind < bgm->lenght)
						putGQueue(music_queue, &(bgm->notes[song_note_ind++]));
				}
				break;
				
			case PAUSE:
				if(ms_count-- == 0)
					state = STOPPED;
				break;
		}
	}
	
	outportb(PIC1_CMD, EOI);
	outportb(PIC2_CMD, EOI);
}

void desenhar_tabuleiro(char *base){
	int i,j;
	
	for(i = 0; i < 20; i++)
		for(j = 0.15*VRES; j < VRES; j++){
		set_pixel(i, j, 15, base);
	
	}
	
	for(i = HRES-20; i < HRES; i++)
		for(j = 0.15*VRES; j < VRES; j++){
		set_pixel(i, j, 15, base);
	
	}
	
	for(i = 0; i < HRES; i++)
		for(j = 0.15*VRES; j < (0.15*VRES + 22); j++){
		set_pixel(i, j, 15, base);
	
	}
	
	for(i = 0; i < HRES; i++)
		for(j = VRES-20; j < VRES; j++){
		set_pixel(i, j, 15, base);
	
	}
		
	for(i = 20; i < HRES - 20; i++)
		for(j = (0.15*VRES + 23); j < VRES-20; j++){
		set_pixel(i, j, 0, base);
	
	}

}

void clear_pontuacoes(char *base){
	int i,j;
	for(i = 0; i < HRES; i++)
		for(j = 0; j < 39; j++){
		set_pixel(i, j, 0, base);
	
	}

}


void song_pre_load_queue(int n_notes)
{
	for(song_note_ind = 0; song_note_ind < n_notes; song_note_ind++)
		putGQueue(music_queue, &(bgm->notes[song_note_ind]));
}

void songinit()
{
	music_queue = newGQueue(10, sizeof(Note));
	//load_notes_queue();
	bgm = song_load("WOC.txt");
	song_pre_load_queue(3);
	
	timer_init(2, LCOM_MODE);
	install_c_irq_handler(RTC_IRQ, rtc_isr, &old_rtc_irq);
	rtc_int_enable();
	unmask_pic(RTC_IRQ);
}


void songfinalize()
{
	speaker_off();
	state = INACTIVE;
	
	mask_pic(RTC_IRQ);
	rtc_int_disable();
	reinstall_c_irq_handler(RTC_IRQ, &old_rtc_irq);
	
	song_delete(bgm);
	deleteGQueue(music_queue);
}


int main(int argc, char* argv[])
{

	
	
	// TIMER
	
	_go32_dpmi_seginfo old_irq_timer;
	install_asm_irq_handler(T0_IRQ, asm_wait, &old_irq_timer);
	
	timer_init(0, LCOM_MODE);
	timer_init(2, LCOM_MODE);
	timer_load(0, get_divider(1000.0));
	
	
	// last_scDO
	_go32_dpmi_seginfo old_irq;
	
	if(install_asm_irq_handler(KBD_IRQ, kbd_isr, &old_irq) == -1)
		return 0;
	
	int last_sc = 0;

	srand ( time(NULL) );
	
	int modo = 1;
		
	modo = 0x100 + 2 * modo - 1; //1 -> 0x101  ;  2 -> 0x103  ;  3-> 0x105  ;  4 -> 0x107
	
	
	//----------------------------------------------------------------------------
	
	__dpmi_meminfo map;
	char* video_base = enter_graphics(modo, &map);
	char table[256 * CHR_H];
	
	Jogador player1, player2;
	player1.vidas = 3;
	player2.vidas = 3;
	player1.pontuacao = 0;
	player2.pontuacao = 0;
	
	int k;
	
	//----------------------------------------------------------------------------
	load_char_table(table);
		
	clear_screen(0, video_base);
	
	int flag=0;
	
	Note n;
	n.freq = Mi3;
	n.dur = 500;
	
	queueInit(&keys_queue);
	
	songinit();
	
	do{
		state = STOPPED;
		clear_screen(0, video_base);
		last_sc = 0;
		while(last_sc != 0x1c){
			mili_sleep(100);
			while(!queueEmpty(&keys_queue))
			{	
			//obter o proximo elemento da queue e retira-lo
			last_sc = queueGet(&keys_queue);
			
			}
			if(last_sc == 0x48 || last_sc == 0xc8 ){
				if(flag > 0) flag--;
			}
			if(last_sc == 0x50 || last_sc == 0xd0 ){
				if(flag < 2) flag++;
			}
			
			drawStringAt("TRON", 280, 50, 15, 0, 3, video_base, table);
			if(flag == 0)drawStringAt("Jogar", 280, 150, 0, 15, 2, video_base, table);
			else drawStringAt("Jogar", 280, 150, 15, 0, 2, video_base, table);
			if(flag == 1)drawStringAt("Pontuacoes", 280, 200, 0, 15, 2, video_base, table);
			else drawStringAt("Pontuacoes", 280, 200, 15, 0, 2, video_base, table);
			if(flag == 2)drawStringAt("Sair", 280, 250, 0, 15, 2, video_base, table);
			else drawStringAt("Sair", 280, 250, 15, 0, 2, video_base, table);
		
		}
		if(flag == 1){
			FILE *f;
			Score sc[11];
			int k=0;
			char line[80];
			char * pch;
			f = fopen("pontuacoes.txt", "r");
			if (f==NULL) perror ("Error opening file");
			else{
				while (!feof(f) && k < 10) {
					fgets (line , 80 , f);
					pch = strtok (line," ,");
					sprintf(sc[k].name, "%s", pch);
					pch = strtok(NULL, " ,");
					sscanf (pch,"%d",&sc[k].number);
					k++;
				}
				fclose (f);
			}
			clear_screen(0, video_base);
			drawStringAt("Pontuacoes:", 200, 50, 15, 0, 2, video_base, table);
			for(k = 0; k < 10; k++){
				sprintf(line, "%s, %d", sc[k].name, sc[k].number);
				drawStringAt(line, 250, 100+ k*30, 15, 0, 2, video_base, table);
			
			}
			sec_sleep(3);
		}
		
	}while(flag == 1);
	
	if(flag == 2){
		songfinalize();
		leave_graphics(map);
		reinstall_asm_irq_handler( KBD_IRQ, &old_irq);
		reinstall_asm_irq_handler(T0_IRQ, &old_irq_timer);
		return 0;
	}

	mili_sleep(500);
	clear_screen(0, video_base);
	last_sc = 0;
	flag = 0;
	while(last_sc != 0x1c){
		mili_sleep(100);
		while(!queueEmpty(&keys_queue))
		{	
		//obter o proximo elemento da queue e retira-lo
		last_sc = queueGet(&keys_queue);
		
		}
		if(last_sc == 0x48 || last_sc == 0xc8 ){
			if(flag > 0) flag--;
		}
		if(last_sc == 0x50 || last_sc == 0xd0 ){
			if(flag < 2) flag++;
		}
		
		drawStringAt("TRON", 280, 50, 15, 0, 3, video_base, table);
		if(flag == 0)drawStringAt("Jogador vs Computador", 200, 150, 0, 15, 2, video_base, table);
		else drawStringAt("Jogador vs Computador", 200, 150, 15, 0, 2, video_base, table);
		if(flag == 1)drawStringAt("Jogador 1 vs Jogador 2", 200, 200, 0, 15, 2, video_base, table);
		else drawStringAt("Jogador 1 vs Jogador 2", 200, 200, 15, 0, 2, video_base, table);
		if(flag == 2)drawStringAt("Sair", 200, 250, 0, 15, 2, video_base, table);
		else drawStringAt("Sair", 200, 250, 15, 0, 2, video_base, table);
	
	}
		
	int modojogo = 0;
	
	if(flag == 0)modojogo = 1;
	if(flag == 1)modojogo = 2;
	if(flag == 2){
		songfinalize();
		leave_graphics(map);
		reinstall_asm_irq_handler( KBD_IRQ, &old_irq);
		reinstall_asm_irq_handler(T0_IRQ, &old_irq_timer);
		return 0;
	}
	
	songfinalize();
	
	clear_screen(0, video_base);
	
	desenhar_tabuleiro(video_base);
	

	drawStringAt("HighScore", 5, 5, 9, 0, 1, video_base, table);
	drawIntAt(player1.pontuacao, 5, 20, 9, 0, 1, video_base, table);
	drawStringAt("HighScore", HRES-72, 5, 12, 0, 1, video_base, table);
	drawIntAt(player2.pontuacao, HRES-72, 20, 12, 0, 1, video_base, table);
	for(k = 0; k < 3; k++){
		player1.vimg[k] = create_sprite(picmota2,5 + k*40 ,40,0,0);
		show_sprite(player1.vimg[k], video_base);
	}
	for(k = 0; k < 3; k++){
		player2.vimg[k] = create_sprite(picmota,530 + k*40 ,40,0,0);
		show_sprite(player2.vimg[k], video_base);
	}
			
	
	Sprite *jog1;
	Sprite *jog2;
	Sprite *colis;
	
	jog1 = create_sprite(picmotanx, HRES/2, (0.15*VRES + VRES- 2)/2, 0, 0);
	jog2 = create_sprite(picmota2x, HRES/2, (0.15*VRES + VRES- 2)/2, 0, 0);
	jog2->dirx=1;
	jog2->diry=0;
	jog1->dirx=-1;
	jog1->diry=0;
	show_sprite(jog1, video_base);
	show_sprite(jog2, video_base);
	
	int coli1, coli2;
	int tempoinicial=0;
	int tempofinal = 0;
	char tempo[10];
	int v;
	while( player1.vidas != 0 && player2.vidas != 0){
		tempoinicial = get_second_of_day();
		
		jog2->dirx=1;
		jog2->diry=0;
		jog1->dirx=-1;
		jog1->diry=0;
		
		if(modojogo == 1){
			while((coli1 = check_colision(jog1, video_base)) == 0 && (coli2 = check_colision(jog2, video_base)) == 0  ){
				last_sc = 0;
				tempofinal = get_second_of_day();
				tempofinal -= tempoinicial;
				if( tempofinal > 60){
					drawStringAt("       ", 300, 10, 15, 0, 2, video_base, table);
					v = tempofinal / 60;
					tempofinal -= v*60;
					sprintf(tempo, "%d:%d", v, tempofinal % 60 ); 
					drawStringAt(tempo, 300, 10, 15, 0, 2, video_base, table);
				}
				else{
					sprintf(tempo, "0:%d", tempofinal % 60); 
					drawStringAt(tempo, 300, 10, 15, 0, 2, video_base, table);
				}
				
				animate_sprite(jog1, 1, 1,  video_base);
				animate_sprite(jog2, 2, 4,  video_base);
				mili_sleep(100);
				while(!queueEmpty(&keys_queue)){	
					//obter o proximo elemento da queue e retira-lo
					last_sc = queueGet(&keys_queue);
					if(last_sc == 0x48 ){
						if(jog1->diry != 1){
							jog1->diry = -1;
							jog1->dirx = 0;
						}
					}
					if(last_sc == 0x50 ){
						if(jog1->diry != -1){
							jog1->diry = 1;
							jog1->dirx = 0;
						}
					}
					if(last_sc == 0x4b ){
						if(jog1->dirx != 1){
							jog1->dirx = -1;
							jog1->diry = 0;
						}
					}
					if(last_sc == 0x4d ){
						if(jog1->dirx != -1){
							jog1->dirx = 1;
							jog1->diry = 0;
						}
					}
				}

				int ddirx=0,ddirnx=0,ddiry=0,ddirny=0;

				if(jog2->dirx == 1) ddirnx = 1;
				if(jog2->dirx == -1) ddirx = 1;
				if(jog2->diry == -1) ddirny = 1;
				if(jog2->diry == 1) ddiry = 1;
				
				while(check_colision(jog2, video_base) != 0){
				
					if(ddirx == 1 && ddirnx == 1 && ddiry == 1 && ddirny == 1)break;
					int randm;
					randm = rand()%4;
					
						switch( randm )
						{
							case 0:
								jog2->dirx = -1;
								jog2->diry = 0; 
								ddirnx = 1;
								break;
							case 1:
								jog2->dirx = 1;
								jog2->diry = 0;
								ddirx = 1;
								break;
							case 2:
								jog2->diry = 1;
								jog2->dirx = 0;
								ddirny =1;
								break;
							case 3:
								jog2->diry = -1;
								jog2->dirx = 0;
								ddiry = 1;
								break;
							default:
								break;
						}

				}
				
			}
		}
		
		if(modojogo == 2){
			while((coli1 = check_colision(jog1, video_base)) == 0 && (coli2 = check_colision(jog2, video_base)) == 0  ){
				last_sc = 0;
				tempofinal = get_second_of_day();
				tempofinal -= tempoinicial;
				if( tempofinal > 60){
					drawStringAt("       ", 300, 10, 15, 0, 2, video_base, table);
					v = tempofinal / 60;
					tempofinal -= v*60;
					sprintf(tempo, "%d:%d", v, tempofinal % 60 ); 
					drawStringAt(tempo, 300, 10, 15, 0, 2, video_base, table);
				}
				else{
					sprintf(tempo, "0:%d", tempofinal % 60); 
					drawStringAt(tempo, 300, 10, 15, 0, 2, video_base, table);
				}
				
				animate_sprite(jog1, 1, 1,  video_base);
				animate_sprite(jog2, 2, 4,  video_base);
				mili_sleep(100);
				while(!queueEmpty(&keys_queue)){	
					//obter o proximo elemento da queue e retira-lo
					last_sc = queueGet(&keys_queue);
					if(last_sc == 0x48  ){
						if(jog1->diry != 1){
							jog1->diry = -1;
							jog1->dirx = 0;
						}
					}
					if(last_sc == 0x50 ){
						if(jog1->diry != -1){
							jog1->diry = 1;
							jog1->dirx = 0;
						}
					}
					if(last_sc == 0x4b  ){
						if(jog1->dirx != 1){
							jog1->dirx = -1;
							jog1->diry = 0;
						}
					}
					if(last_sc == 0x4d ){
						if(jog1->dirx != -1){
							jog1->dirx = 1;
							jog1->diry = 0;
						}
					}
					
					//jogador 2
					if(last_sc == 0x11  ){
						if(jog2->diry != 1){
							jog2->diry = -1;
							jog2->dirx = 0;
						}
					}
					if(last_sc == 0x1f  ){
						if(jog2->diry != -1){
							jog2->diry = 1;
							jog2->dirx = 0;
						}
					}
					if(last_sc == 0x1e  ){
						if(jog2->dirx != 1){
							jog2->dirx = -1;
							jog2->diry = 0;
						}
					}
					if(last_sc == 0x20 ){
						if(jog2->dirx != -1){
							jog2->dirx = 1;
							jog2->diry = 0;
						}
					}
					
					
				}
				
			}
		}
		
		//
		
		if(coli1 !=0){
			colis = create_sprite(pic2, (jog1->x - 12),  (jog1->y - 6), 0, 0);
			show_sprite(colis, video_base);
			play_note(&n);
			sec_sleep(2);
			
			player1.vidas--;
			destroy_sprite(player1.vimg[player1.vidas], video_base);
			tempofinal = get_second_of_day();
			tempofinal -= tempoinicial;
			if(coli1 == 2){
				player2.pontuacao += 1000*tempofinal + (1000*tempofinal)/5;
				
			}
			else{
				player2.pontuacao += 1000*tempofinal + (1000*tempofinal)/10;
			}
			player1.pontuacao += 1000*tempofinal;
			
		}
		if(coli2 !=0){
			colis = create_sprite(pic2, (jog2->x - 12),  (jog2->y - 6), 0, 0);
			show_sprite(colis, video_base);
			play_note(&n);
			sec_sleep(2);
			
			player2.vidas--;
			destroy_sprite(player2.vimg[player2.vidas], video_base);
			tempofinal = get_second_of_day();
			tempofinal -= tempoinicial;
			if(coli1 == 1){
				player1.pontuacao += 1000*tempofinal + (1000*tempofinal)/5;
			}
			else{
				player1.pontuacao += 1000*tempofinal + (1000*tempofinal)/10;
			}
			player2.pontuacao += 1000*tempofinal;
		
		}
		desenhar_tabuleiro(video_base);
		clear_pontuacoes(video_base);
		jog1->x = HRES/2;
		jog1->y = (0.15*VRES + VRES- 2)/2;
		jog2->x = HRES/2;
		jog2->y = (0.15*VRES + VRES- 2)/2;
		jog2->dirx=1;
		jog2->diry=0;
		jog1->dirx=-1;
		jog1->diry=0;
		drawStringAt("HighScore", 5, 5, 9, 0, 1, video_base, table);
		drawIntAt(player1.pontuacao, 5, 20, 9, 0, 1, video_base, table);
		drawStringAt("HighScore", HRES-72, 5, 12, 0, 1, video_base, table);
		drawIntAt(player2.pontuacao, HRES-72, 20, 12, 0, 1, video_base, table);
		
		tempofinal = get_second_of_day();
		
	}
	
	clear_screen(0, video_base);
	
	drawStringAt("GAME OVER", 250, 190, 15, 0, 2, video_base, table);
	if(player1.pontuacao > player2.pontuacao){
		drawStringAt("Player 1 Wins", 220, 250, 15, 9, 2, video_base, table);
		sec_sleep(2);
		leave_graphics(map);
		reinstall_asm_irq_handler( KBD_IRQ, &old_irq);
		reinstall_asm_irq_handler(T0_IRQ, &old_irq_timer);
		FILE *f;
		Score sc[11];
		int k=0;
		char line[80];
		char * pch;
		f = fopen("pontuacoes.txt", "r");
		if (f==NULL) perror ("Error opening file");
		else{
			while (!feof(f) && k < 10) {
				fgets (line , 80 , f);
				pch = strtok (line," ,");
				sprintf(sc[k].name, "%s", pch);
				pch = strtok(NULL, " ,");
				sscanf (pch,"%d",&sc[k].number);
				k++;
			}
			fclose (f);
		}
		if(sc[9].number < player1.pontuacao){
			sc[10].number = player1.pontuacao;
			printf("Escreva o seu nome: ");
			gets(line);
			pch = strtok(line, " -_");
			sprintf(sc[10].name, "%s", pch);
			qsort((void *) &sc, 11, sizeof(Score), (compfn)compare);
			f = fopen("pontuacoes.txt", "w+");
			for(k = 0; k < 10; k++){
				sprintf(line, "%s, %d\n", sc[k].name, sc[k].number);
				fwrite(line, strlen(line), 1, f);

			}
			fclose(f);
		}
		
		for(k = 0; k < 10; k++){
			printscore(sc[k]);
		}
		
	}
	else{
		drawStringAt("Player 2 Wins", 220, 250, 15, 12, 2, video_base, table);
		sec_sleep(2);
		leave_graphics(map);
		reinstall_asm_irq_handler( KBD_IRQ, &old_irq);
		reinstall_asm_irq_handler(T0_IRQ, &old_irq_timer);
		FILE *f;
		Score sc[11];
		int k=0;
		char line[80];
		char * pch;
		f = fopen("pontuacoes.txt", "r");
		if (f==NULL) perror ("Error opening file");
		else{
			while (!feof(f) && k < 10) {
				fgets (line , 80 , f);
				pch = strtok (line," ,");
				sprintf(sc[k].name, "%s", pch);
				pch = strtok(NULL, " ,");
				sscanf (pch,"%d",&sc[k].number);
				k++;
			}
			fclose (f);
		}
		if(sc[9].number < player2.pontuacao){
			sc[10].number = player2.pontuacao;
			printf("Escreva o seu nome: ");
			gets(line);
			pch = strtok(line, " -_");
			sprintf(sc[10].name, "%s", pch);
			qsort((void *) &sc, 11, sizeof(Score), (compfn)compare);
			f = fopen("pontuacoes.txt", "w+");
			for(k = 0; k < 10; k++){
				sprintf(line, "%s, %d\n", sc[k].name, sc[k].number);
				fwrite(line, strlen(line), 1, f);

			}
			fclose(f);
		}
		
		for(k = 0; k < 10; k++){
			printscore(sc[k]);
		}
		
	}
	
	return 0;
}

void printscore(Score sc){
	printf("%s, %d\n", sc.name, sc.number);
}

int compare(Score *elem1, Score *elem2)
{
   if ( elem1->number > elem2->number)
      return -1;

   else if (elem1->number < elem2->number)
      return 1;

   else
      return 0;
}
